// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <gtest/gtest.h>
#include <climits>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cantor/utility/json_reader.h>
#include <cantor/unresolved/unresolved.h>
#include <cantor/unresolved/dispatch_expr.h>
#include <cantor/unresolved/stmts/select_stmt.h>
#include <cantor/common/type_list.h>
#include <turbo/log/logging.h>
#include <tests/config.h>
#include <google/protobuf/util/message_differencer.h>

int main(int argc, char *argv[]) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

namespace cantor {

    class DataJson {
    public:
        static DataJson &instance() {
            static DataJson instance;
            return instance;
        }

        const nlohmann::ordered_json &json() const { return json_; }

    private:
        DataJson() {
            std::string json_path = turbo::str_cat(testing::test_root, "/data/create_table.json");
            auto rs = read_file(json_path);
            KCHECK(rs.ok());
            json_ = std::move(rs).value_or_die();
        }

        nlohmann::ordered_json json_;
    };

    TEST(test_parser, case_create_check_table) {
        cantor::unresolved::Unresolved aster;

        std::string sql = "a+b, c%d, (a+b) * c, 1 + 2";
        KLOG(INFO)<<NOPREFIX<<"testing sql: "<<sql;
        auto rs = aster.parse_expressions(sql);
        ASSERT_TRUE(rs.ok());
        KLOG(INFO)<<NOPREFIX<<aster.statements().DebugString();
        auto  j = aster.to_json();
        ASSERT_TRUE(j.ok());
        auto jstr = std::move(j).value_or_die();
        KLOG(INFO)<<NOPREFIX<<j;
        cantor::unresolved::Unresolved aster1;
        ASSERT_TRUE(aster1.from_json(jstr).ok());
        auto j1 = aster1.to_json();
        ASSERT_TRUE(j1.ok());
        auto jstr1 = std::move(j1).value_or_die();
        ASSERT_EQ(jstr, jstr1);
        google::protobuf::util::MessageDifferencer differ;
        ASSERT_TRUE(differ.Compare(aster.statements(), aster1.statements()));
        ASSERT_EQ(aster.result.size(), aster1.result.size());
        ASSERT_EQ(aster.result.size(), 1);

        auto sl = aster.result[0].cast_to<unresolved::UnresolvedSelectStmt>();
        ASSERT_EQ(sl.ok(), true);
        auto st = std::move(sl).value_or_die();
        ASSERT_EQ(st.field_count(),4);
        KLOG(INFO)<<sl->location();
        //ASSERT_EQ(aster.statements(), aster1.statements());
        auto vst = aster.result[0].cast_to(aster.result[0].stmt->type());
        auto ptr = std::get_if<cantor::unresolved::UnresolvedSelectStmt>(&vst);
        ASSERT_TRUE(ptr!=nullptr);

        auto vst1 = aster.result[0].cast_to();
        auto ptr1 = std::get_if<cantor::unresolved::UnresolvedSelectStmt>(&vst1);
        ASSERT_TRUE(ptr!=nullptr);
        ASSERT_EQ(typeid(*ptr).name(), typeid(*ptr1).name());
        auto dlist = cantor::unresolved::get_definite_list();
        std::cout<<"definite list: [\n";
        bool not_first = false;
        for (auto &it : dlist) {
            std::cout<<"\t"<<cantor::pb::ast::ExprNodeType_Name(it)<<std::endl;
        }
        std::cout<<"]"<<std::endl;

        auto udlist = cantor::unresolved::get_dispatch_list();
        std::cout<<"dipatch list: [\n";
        not_first = false;
        for (auto &it : udlist) {
            std::cout<<"\t"<<cantor::pb::ast::ExprNodeType_Name(it)<<std::endl;
        }
        std::cout<<"]"<<std::endl;

        auto v = cantor::unresolved::ListExprType<cantor::unresolved::DqlDefiniteListVisitorType>::get_list();
        std::cout<<"dql definite list: [";
        not_first = false;
        for (auto &it : v) {
            if (not_first) {
                std::cout<<", ";
            } else {
                not_first = true;
            }
            std::cout<<cantor::pb::ast::ExprNodeType_Name(it);
        }
        std::cout<<"]"<<std::endl;
    }
    static_assert(unresolved::isInAllExprList<unresolved::UnresolvedAlgorithmOpt>::value, "not in all visitor lists");
}  // namespace cantor
